#include "session.h"
#include <errno.h>
#include "net_define.h"
#include "base/core/os/debug.h"
#include "base/core/os/string.h"
#include "base/core/log.h"
#include "base/core/Profiler.h"

#ifdef _MSC_VER
#pragma warning(disable : 4018)
#endif

namespace 
{
    static const uint32_t s_send_threshold = 256;
}

Session::Session()
	: m_state(SESSION_STATE_NULL),
	m_eventData(),
    m_startTime(0),
    m_activeTime(0),
    m_socket(INVALID_SOCKET),
    m_recvBuffer(),
    m_sendBuffer(),
    m_userData(),
    m_lruNode(this),
    m_localPort(0),
    m_mark(0xFF),
    m_timeout(0xFFFFFFFF),
    m_encrypt(false) 
{
    m_eventData.session = this;
    m_eventData.sock_flag = SOCKET_ACCEPT;
    memset(&m_remote, 0, sizeof(m_remote));
    m_localPort = 0;
	m_isShutDown = false;
}

Session::~Session() 
{}

bool Session::Init(SocketFlag  flag,
                   SOCKET      sock,
                   uint16_t    local_port,
                   uint64_t    start_time,
                   RawAllocator* alloc,
                   void*        handle,
                   EventDataEx* data) 
{
    bool result = false;
    int retCode = -1;
    char IP[32];

    MMOLOG_ASSERT(sock != INVALID_SOCKET);
    MMOLOG_ASSERT(alloc);

    m_socket = sock;
    start_time = start_time;
    m_activeTime = start_time;
    m_localPort = local_port;
    m_state = SESSION_STATE_WORKING;
    
    retCode = m_recvBuffer.Init(alloc, data->recvBufSize);
    MMOLOG_ASSERT_EXIT(retCode);

    retCode = m_sendBuffer.Init(alloc, data->sendBufSize);
    MMOLOG_ASSERT_EXIT(retCode);

    m_eventData.sock_flag = flag;
    m_eventData.handle = handle;
    m_eventData.subpackage = data->subpackage;

    retCode = Socket::GetPeerAddress(m_socket, IP, countof(IP), &m_remote.port);
    MMOLOG_PROCESS_ERROR(retCode);
    
    retCode = MakeIPAddress(&m_remote, IP, m_remote.port);
    MMOLOG_PROCESS_ERROR(retCode);
    
    result = true;
Exit0:
    return result;
}

bool Session::UnInit() 
{
    bool result  = false;
    bool retCode = false;

    retCode = Close();
    MMOLOG_PROCESS_ERROR(retCode);

    m_localPort = 0;
    m_startTime = 0;
    m_activeTime = 0;
    m_recvBuffer.Release();
    m_sendBuffer.Release();
    m_state = SESSION_STATE_NULL;
    memset(&m_remote, 0, sizeof(m_remote));

    result = true;
Exit0:
    return result;
}

bool Session::Close() 
{
    bool result = false;
    bool retCode = false;

    if (m_socket != INVALID_SOCKET) 
	{
        retCode = Socket::Close(m_socket);
        MMOLOG_PROCESS_ERROR(retCode);
        m_socket = INVALID_SOCKET;
    }
    result = true;
Exit0:
    return result;
}

int Session::Shutdown() 
{
	if (m_state == SESSION_STATE_SHUTDOWN && m_isShutDown)
	{
		return 0;
	}

    int retCode = -1;
    MMOLOG_PROCESS_ERROR(m_socket != INVALID_SOCKET);
    if (IsSendComplete()) 
	{
        Socket::Shutdown(m_socket, SHUTDOWN_SEND);
		m_isShutDown = true;
    }

	m_state = SESSION_STATE_SHUTDOWN;
    retCode = 0;
Exit0:
    return retCode;
}

bool Session::Send(const char* buf, uint32_t len, bool raw) 
{
    PROFILE_SEGMENT("Session::Send");

    int retCode = false;
    // write to send buffer
    if (len > 0) 
	{
        retCode = m_sendBuffer.Write(buf, len, (raw ? false : m_encrypt));
        if (retCode < 0) 
		{
            LogErrFmtPrint("[Net] send buffer is full write len %u", len);
            return false;
        }
    }
    // send
    if (len == 0 || m_sendBuffer.GetReadLength() > s_send_threshold) 
	{
        retCode = Socket::Send(m_socket, m_sendBuffer.GetReadPtr(), m_sendBuffer.GetReadLength());
        if (retCode < 0) 
		{
            LogErrFmtPrint("[Net] send error remote[%s]", m_remote.IPAndPort);
            return false;
        } 
		else if (retCode > 0) 
		{
            m_sendBuffer.Read(retCode);
        }
    }
    return true;
}

int Session::Recv(uint64_t tick_ms) 
{
	PROFILE_SEGMENT("Session::Recv");

	m_recvBuffer.Resize();
	int ret = Socket::Recv(m_socket, m_recvBuffer.GetWritePtr(), m_recvBuffer.GetWriteSpace());
	if (ret < 0)
	{// err
		if (ret == -2) 
		{// EAGAIN/WSAEWOULDBLOCK
			m_activeTime = tick_ms;
			return 0;
		}
		return -1;
	} 
	else if (ret == 0) 
	{// remote close
		return 1;
	} 
	else 
	{// success
		m_recvBuffer.Write(static_cast<uint32_t>(ret), m_encrypt);
		m_activeTime = tick_ms;
		return 0;
	}
}

bool Session::ReadRecvData(uint32_t len) 
{
    PROFILE_SEGMENT("Session::ReadRecvData");
    return m_recvBuffer.Read(len);
}

bool Session::IsWorking() 
{
    return m_state == SESSION_STATE_WORKING;
}

bool Session::IsSendComplete() 
{
    return (m_sendBuffer.GetReadLength() == 0);
}

bool Session::IsFreeTimeout() 
{
    return m_timeout != 0xFFFFFFFF;
}

bool Session::IsTimeout(uint64_t tick_ms) 
{
    bool result = true;
    MMOLOG_ASSERT_EXIT(m_timeout != 0xFFFFFFFF);
    result = (m_activeTime + m_timeout < tick_ms);
Exit0:
    return result;
}

void Session::SetTimeout(uint32_t tm) 
{
    m_timeout = tm;
}

SOCKET Session::GetSocket() 
{
    return m_socket;
}

SocketFlag Session::GetSocketFlag() 
{
    return m_eventData.sock_flag;
}

EventData* Session::GetEventData() 
{
    return &m_eventData;
}

DoubleNode* Session::GetListNode() 
{
    return &m_lruNode;
}

uint32_t Session::GetTimeout() 
{
    return m_timeout;
}

uint64_t Session::GetActiveTime() 
{
    return m_activeTime;
}

bool Session::GetRecvData(char** data, uint32_t* len) 
{
    *data = m_recvBuffer.GetReadPtr();
    *len  = m_recvBuffer.GetReadLength();
    return true;
}

void* Session::GetSubpackage() 
{
    return m_eventData.subpackage;
}

void Session::GetUserReq(void** retReq, void** retContext) 
{
    *retReq     = m_eventData.user_req;
    *retContext = m_eventData.context;
}

void Session::SetUserReq(void* req, void* context) 
{
    m_eventData.user_req = req;
    m_eventData.context  = context;
}

const SESSION_USER_DATA& Session::GetUserData() 
{
    return m_userData;
}

void Session::SetUserData(const SESSION_USER_DATA& data) 
{
    m_userData = data;
}

const IP_ADDRESS& Session::GetRemoteAddress() 
{
    return m_remote;
}

uint16_t Session::GetLocalPort() 
{
    return m_localPort;
}

void* Session::GetNetPackHandle() 
{
    return m_eventData.netpackhandle;
}

void Session::SetNetPackHandle(void* netpackhandle) 
{
    m_eventData.netpackhandle = netpackhandle;
}

uint16_t Session::GetMark() 
{
    return m_mark;
}

void Session::SetMark(uint16_t mark) 
{
    m_mark = mark;
}

bool Session::GetEncrypt() 
{
    return m_encrypt;
}

void Session::SetEncrypt(bool encrypt) 
{
    m_encrypt = encrypt;
}

bool MakeIPAddress(IP_ADDRESS *IPAddress, const char *IP, uint16_t port) 
{
    bool result = false;
    int length = 0;

    MMO_ASSERT_EXIT(IPAddress);
    MMO_ASSERT_EXIT(IP);
    
    length = (int)strlen(IP);
    MMOLOG_ASSERT_EXIT(length < (int)countof(IPAddress->IP));
    memcpy(IPAddress->IP, IP, length + 1);
    IPAddress->port = port;
    length = snprintf(IPAddress->IPAndPort, countof(IPAddress->IPAndPort), "%s:%u", IP, port);
    MMOLOG_ASSERT_EXIT((length > 0) && (length < (int)countof(IPAddress->IPAndPort)));
    result = true;
Exit0:
    return result;
}
